package net.itxxw.manage_cms.service;

import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import net.itxxw.framework.domain.cms.CmsConfig;
import net.itxxw.framework.domain.cms.CmsPage;
import net.itxxw.framework.domain.cms.CmsTemplate;
import net.itxxw.framework.domain.cms.response.CmsCode;
import net.itxxw.framework.exception.ExceptionCast;
import net.itxxw.manage_cms.dao.CmsConfigRepository;
import net.itxxw.manage_cms.dao.CmsPageRepository;
import net.itxxw.manage_cms.dao.CmsTemplateRepository;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;

import org.springframework.http.HttpEntity;

import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Service
public class CmsConfigService {
    @Autowired
    CmsConfigRepository cmsConfigRepository;

    @Autowired
    CmsPageRepository cmsPageRepository;

    @Autowired
    RestTemplate restTemplate;

    @Autowired
    CmsTemplateRepository cmsTemplateRepository;

    @Autowired
    GridFsTemplate gridFsTemplate;

    @Autowired
    GridFSBucket gridFSBucket;

    @Autowired
    PageService pageService;

    //根据id查询配置管理信息
    public CmsConfig getConfigById(String id){
        Optional<CmsConfig> optional = cmsConfigRepository.findById(id);
        if(optional.isPresent()){
            CmsConfig cmsConfig = optional.get();
            return cmsConfig;
        }
        return null;
    }

    //页面静态化方法
    /**
     静态化程序获取页面的DataUrl
     *
     * 静态化程序远程请求DataUrl获取数据模型。
     *
     * 静态化程序获取页面的模板信息
     *
     * 执行页面静态化
     */

    public String getPageHtml(String pageId) throws IOException, TemplateException{
        //获取页面数据模型
        Map model= this.getModelByPageId(pageId);
        if(model==null){
            //获取页面模型数据为空
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL);
        }

        //获取页面模板信息
        String templateContent = getTemplateByPageId(pageId);
        //有这个模板，就执行静态化模板，生成新的Html

        if(StringUtils.isEmpty(templateContent)){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
        }

        //执行页面静态化
        //参数1，静态模板，参数2，数据模型对象
        String html=generateHtml(templateContent,model);

        if(StringUtils.isEmpty(html)){
            ExceptionCast.cast(CmsCode.CMS_GENRATEHTML_HTML_IS_NULL);
        }

        return html;
    }

    //执行静态化,通过页面模板和数据模型，生成静态化页面数据
    public String generateHtml(String templateContent, Map model) {
        //创建配置对象
        Configuration configuration = new Configuration(Configuration.getVersion());
        //创建模板加载器
        StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
        stringTemplateLoader.putTemplate("template",templateContent);
        //向configuration配置模板加载器
        configuration.setTemplateLoader(stringTemplateLoader);
        //获取模板
        Template template1 = null;
        try {
            template1 = configuration.getTemplate("template");
        } catch (IOException e) {
            e.printStackTrace();
        }
        String html = null;
        try {
            html = FreeMarkerTemplateUtils.processTemplateIntoString(template1, model);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
        return html;
    }

    //获取页面的模板信息的方法
    public String getTemplateByPageId(String pageId) {
        //根据页面id,取页面对象
        CmsPage cmsPage =pageService.findCmsPageById(pageId);
        if (cmsPage == null) {
            //为空，抛异常，页面不存在
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
        }
        //从页面信息中拿模板的id
        String templateId = cmsPage.getTemplateId();
        if (StringUtils.isEmpty(templateId)){
            //如果模板id属性为空，就抛异常,页面模板为空
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
        }
        //通过模板信息管理类，查询模板信息
        Optional<CmsTemplate> optional = cmsTemplateRepository.findById(templateId);
        if (optional.isPresent()){
            //如果有数据，就把模板数据取出来
            CmsTemplate cmsTemplate = optional.get();
            //再取模板文件的id
            String templateFileId = cmsTemplate.getTemplateFileId();
            //根据模板文件的id,查询gridfs文件管理器里面的GridFSFile文件对象
            //通过，模板id,去fs.files中的”——id“的列中查找，ftl文件名对应fs.chunks集合表中的流数据
            GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(templateFileId)));
            //通过配置类中，配置好的下载器。去下载这个GridFSFile文件对象
            // 解释：开启一个下载流，通过grid文件id,去下载
            GridFSDownloadStream gridFSDownloadStream =
                    gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
            //创建GridFsResource对象，获取流,
            // 参数1：文件管理器的路径，参数2：配置好的下载流
            GridFsResource gridFsResource =
                    new GridFsResource(gridFSFile,gridFSDownloadStream);
            //从流中取数据,把流文件用utf-8的字节解析为字符串
            try {
                String content = IOUtils.toString(gridFsResource.getInputStream(), "utf-8");
                //返回字符串页面
                return content;
            } catch (IOException e) {
                e.printStackTrace();
            }

        } //取模型页面完成
        //如果查询到的模板信息为空，就不去取模板
        return null;
    }


    /**
     * 获取页面模型数据
     * @param pageId
     * @return  body 获取模型数据
     */
    //获取页面模型数据
    public Map getModelByPageId(String pageId) {
        //查询页面信息
        CmsPage cmsPage = this.pageService.findCmsPageById(pageId);
        if(cmsPage==null){
            //页面不存在
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
        }
        //取出dataURI
        String dataUrl = cmsPage.getDataUrl();
        if(StringUtils.isEmpty(dataUrl)){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAURLISNULL);
        }

        //通过获取当前请求的request对象来取出jwt认证信息,并且传递到下一个请求中
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String jwt = request.getHeader("authorization");
        //使用LinkedMultiValueMap储存多个header信息
        LinkedMultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        headers.add("authorization",jwt);
        HttpEntity<MultiValueMap<String, String>> httpEntity= new HttpEntity<>(headers);
        //发送请求获取模型数据
        ResponseEntity<Map> forEntity = restTemplate.exchange(dataUrl, HttpMethod.GET,httpEntity,Map.class);
        //得到模型数据后，转换为json格式封装到另一个map中。
       Map body = forEntity.getBody();

        //返回json格式的模板信息Map
        return body;
    }


}